package com.stottlerhenke.simbionic.editor;

import java.awt.Color;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.util.Iterator;
import java.util.List;

import javax.swing.Icon;
import javax.swing.ImageIcon;

import com.stottlerhenke.simbionic.editor.gui.ComponentRegistry;
import com.stottlerhenke.simbionic.editor.gui.SB_Line;
import com.stottlerhenke.simbionic.editor.gui.SB_Output;
import com.stottlerhenke.simbionic.editor.gui.SB_OutputBar;
import com.stottlerhenke.simbionic.editor.gui.SB_ProjectBar;
import com.stottlerhenke.simbionic.editor.parser.SB_ArrayNode;
import com.stottlerhenke.simbionic.editor.parser.SB_ErrorNode;
import com.stottlerhenke.simbionic.editor.parser.SB_ParseNode;

public class SB_Global extends SB_Variable {
    public static final Class SB_GlobalClass = com.stottlerhenke.simbionic.editor.SB_Global.class;

    private static final long serialVersionUID = 2302585093L + 7;

    protected String _initial; // initial value

    protected boolean _polymorphic = false;

    protected static ImageIcon _polyIcon = null;

    protected static Class[] _classes;

    public SB_Global() // constructor for Externalizable object
    {
    }

    public SB_Global(String name) {
        super(name);
        _initial = "";
    }

    public SB_Global(String name, String initial, int type) {
        super(name);
        _initial = initial;
        _type = type;
    }

    public String getInitialValue() {
        return _initial;
    }

    public boolean isPolymorphic() {
        return _polymorphic;
    }

    public boolean isCellEditable() {
        return _editable && !_polymorphic;
    }

    public Icon getIcon() {
        if (_polymorphic) {
            if (_polyIcon == null)
                _polyIcon = Util.getImageIcon("PolyGlobal.gif");
            return _polyIcon;
        } else {
            if (_icon == null)
                _icon = Util.getImageIcon("Global.gif");
            return _icon;
        }
    }

    protected Class[] getRelatedClasses() {
        if (_classes == null) {
            _classes = new Class[2];

            _classes[0] = SB_GlobalClass;
            _classes[1] = SB_Constant.SB_ConstantClass;

        }
        return _classes;
    }

    public void compileSIM(SimBionicEditor editor, BufferedWriter out,
            SB_ErrorInfo errorInfo, SB_TypeManager typeManager) {
        try {
            super.compileSIM(out, typeManager);
            out.write(" ");

            if ((_type == kEntity) || (_type == kData))
            {
                // user does not specify initial value for entity or data
                out.write("0");
                return;
            }
            
            SB_OutputBar outputBar = SB_OutputBar.getInstance();
            SB_Output build = outputBar.getBuild();

            if (_initial.length() == 0)
            {
                // must specify an initial value -- give a more helpful error message
                errorInfo._ne++;
                build.addLine(new SB_Line("ERROR: '" + getName()
                        + "' global: no initial value specified", Color.red, null,
                        null, this, SB_Line.ERROR));                
                return;
            }
            
            // parse initial expression
            SB_ProjectBar projectBar = (SB_ProjectBar) ComponentRegistry.getProjectBar();
            String initial = projectBar.getCatalog().constantReplace(_initial);
            SB_ParseNode ptree = parseValue(initial);
            if (ptree != null && !ptree.IsValid()) {

                SB_ErrorNode err = null;
                while ((err = ptree.GetNextError(err)) != null) {
                    errorInfo._ne++;
                    build.addLine(new SB_Line("ERROR: '" + getName()
                            + "' global: " + err.GetMsg(), Color.red, null,
                            null, this, SB_Line.ERROR));
                }
                return;
            }

            switch (_type) {
            case kFloat:
            case kInteger:
            case kBoolean:
            case kString:
                out.write(SB_ProjectBar.getPString(initial));
                break;

            case kVector:
                out.write(initial);
                break;
                
            case kArray:
                compileArrayInitValue(editor,out,ptree,errorInfo,typeManager);
                break;
                
            case kTable:
                compileTableInitValue(editor,out,ptree,errorInfo,typeManager);
                break;
            }
        } catch (IOException exception) {
            System.err.println("i/o exception");
        }
    }

    /**
     * Writes the initial values for a global of type array.
     * @param out
     * @param ptree 
     * @param errorInfo
     * @throws IOException 
     */
    protected void compileArrayInitValue(SimBionicEditor editor,BufferedWriter out,SB_ParseNode ptree, SB_ErrorInfo errorInfo,SB_TypeManager typeManager) throws IOException
    {
        SB_OutputBar outputBar = SB_OutputBar.getInstance();
        SB_Output build = outputBar.getBuild();

        if (!(ptree instanceof SB_ArrayNode))
        {
            errorInfo._ne++;
            build.addLine(new SB_Line("ERROR: '" + getName() + "' global: initial value '" + ptree.ToString() + "' is not an array.", 
                                        Color.red, null, null, this, SB_Line.ERROR));
            return;
        }

        // write out size of array
        List elements = ((SB_ArrayNode)ptree).getArray();
        out.write(elements.size() + "\n");
        
        // write out the elements of the array, one per line
        Iterator elementIt = elements.iterator();
        int i=0;
        while (elementIt.hasNext())
        {
            SB_ParseNode elementNode = (SB_ParseNode)elementIt.next();
            SB_Global tempVar = new SB_Global(getName()+"["+i+"]",elementNode.ToString(), typeManager.convertToOldId(elementNode.GetType()));
            
            out.write("\t\t\t");
            tempVar.compileSIM(editor,out,errorInfo,typeManager);
            if (elementIt.hasNext())
                out.write("\n");
            ++i;
        }
    }

    /**
     * Writes the initial values for a global of type table.
     * @param out
     * @param ptree 
     * @param errorInfo
     * @throws IOException 
     */
    protected void compileTableInitValue(SimBionicEditor editor,BufferedWriter out,SB_ParseNode ptree, SB_ErrorInfo errorInfo,SB_TypeManager typeManager) throws IOException
    {
        SB_OutputBar outputBar = SB_OutputBar.getInstance();
        SB_Output build = outputBar.getBuild();

        if (!(ptree instanceof SB_ArrayNode))
        {
            errorInfo._ne++;
            build.addLine(new SB_Line("ERROR: '" + getName() + "' global: initial value '" + ptree.ToString() + "' is not a table.", 
                                        Color.red, null, null, this, SB_Line.ERROR));
            return;
        }

        // write out number of rows
        List rows = ((SB_ArrayNode)ptree).getArray();
        out.write(rows.size() + "\n");
        
        // write out the rows of the table
        Iterator rowIt = rows.iterator();
        int row=0;
        while (rowIt.hasNext())
        {
            SB_ParseNode rowNode = (SB_ParseNode)rowIt.next();
            
            if (!(rowNode instanceof SB_ArrayNode))
            {
                errorInfo._ne++;
                build.addLine(new SB_Line("ERROR: '" + getName() + "' global: initial value '" + ptree.ToString() + "' is not a table.", 
                                            Color.red, null, null, this, SB_Line.ERROR));
                return;
            }

            List columns = ((SB_ArrayNode)rowNode).getArray();
            out.write("\t\t\t10 arrayValue 8 " + columns.size() + "\n");
            
            // write out the columns of the table, one per line
            Iterator colIt = columns.iterator();
            int col=0;
            while (colIt.hasNext())
            {
                SB_ParseNode colNode = (SB_ParseNode)colIt.next();
                
                SB_Global tempVar = new SB_Global(getName()+"["+row+","+col+"]",colNode.ToString(), typeManager.convertToOldId(colNode.GetType()));
                
                out.write("\t\t\t\t");
                tempVar.compileSIM(editor,out,errorInfo,typeManager);
                if (colIt.hasNext())
                    out.write("\n");
                ++col;
            }
            if (rowIt.hasNext())
                out.write("\n");
            ++row;
        }
    } 
    
    public void writeExternal(ObjectOutput out) throws IOException {
        super.writeExternal(out);

        out.writeObject(_initial);
        out.writeBoolean(_polymorphic);
    }

    public void readExternal(ObjectInput in) throws ClassNotFoundException,
            IOException {
        super.readExternal(in);

        _initial = (String) in.readObject();
        _polymorphic = in.readBoolean();
    }

    /**
     * @return Returns the initial.
     */
    public String getInitial() {
        return _initial;
    }

    /**
     * @param initial
     *            The initial to set.
     */
    public void setInitial(String initial) {
        _initial = initial;
    }

    /**
     * @param polymorphic
     *            The polymorphic to set.
     */
    public void setPolymorphic(boolean polymorphic) {
        _polymorphic = polymorphic;
    }
}